package com.testdocs.server.version;

import java.util.ArrayList;
import java.util.List;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.testdocs.client.error.ObjectAccessErrorException;
import com.testdocs.client.validation.ConstraintViolationException;
import com.testdocs.client.validation.Validator;
import com.testdocs.client.version.VersionInfo;
import com.testdocs.client.version.VersionService;
import com.testdocs.server.persistence.PMF;
import com.testdocs.server.project.Project;
import com.testdocs.server.project.Role;

@SuppressWarnings("serial")
public class VersionServiceImpl extends RemoteServiceServlet implements VersionService {
	private User getUser() {
		return UserServiceFactory.getUserService().getCurrentUser();
	}
	
	private void checkLoggedIn() throws ObjectAccessErrorException {
		if (!UserServiceFactory.getUserService().isUserLoggedIn()) {
			throw new ObjectAccessErrorException();
		}
	}

	@SuppressWarnings("unchecked")
	private Role checkPriveleges(PersistenceManager pm, Project project) throws ObjectAccessErrorException {
		Query q = pm.newQuery(Role.class, "projectId == p && userId == u");
		q.declareParameters("java.lang.String p, java.lang.String u");
		List<Role> roles = (List<Role>) q.execute(project.getId(), getUser().getUserId());
		if (roles.size() != 1){
			throw new ObjectAccessErrorException();
		}
		return roles.get(0);
	}
	
	@SuppressWarnings("unchecked")
	public VersionInfo[] getVersions(String projectId) throws ObjectAccessErrorException {
		checkLoggedIn();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Project project = pm.getObjectById(Project.class, projectId);
			checkPriveleges(pm, project);
			
			Query q = pm.newQuery(Version.class, "projectId == p");
			q.declareParameters("java.lang.String p");
			q.setOrdering("order desc");
			List<Version> versions = (List<Version>) q.execute(project.getId());

			VersionInfo result[] = new VersionInfo[versions.size()];
			int i = 0;
			for (Version version : versions) {
				result[i++] = version.toVersionInfo();
			}

			return result;
		} catch (JDOObjectNotFoundException e) {
			throw new ObjectAccessErrorException();
		} finally {
			pm.close();
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public VersionInfo createVersion(VersionInfo newVersionInfo,
			String basedOnVersionId) throws ObjectAccessErrorException, ConstraintViolationException {
		checkLoggedIn();
		new Validator().validate(newVersionInfo);
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
			Version basedOnVersion = pm.getObjectById(Version.class, basedOnVersionId);
			Project project = pm.getObjectById(Project.class, basedOnVersion.getProjectId());
			checkPriveleges(pm, project);

			int order = basedOnVersion.getOrder();
			
			Version newVersion = new Version(newVersionInfo.getName(), order + 1, project.getId());
			pm.makePersistent(newVersion);
			
			Query q = pm.newQuery(Version.class, "projectId == p && order > o");
			q.declareParameters("java.lang.String p, java.lang.Integer o");
			List<Version> versions = (List<Version>) q.execute(project.getId(), order);

			for (Version version : versions) {
				version.setOrder(version.getOrder() + 1);
			}
			
			return newVersion.toVersionInfo();
		} catch (JDOObjectNotFoundException e) {
			throw new ObjectAccessErrorException();
		} finally {
			if (tx.isActive()) {
				tx.rollback();
			}
			pm.close();
		}
	}

	@Override
	public void remove(VersionInfo versions[]) throws ObjectAccessErrorException {
		checkLoggedIn();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
			List<Version> versionsList = new ArrayList<Version>();
			for (VersionInfo versionInfo : versions) {
				Version version = pm.getObjectById(Version.class, versionInfo.getId());
				Project project = pm.getObjectById(Project.class, version.getProjectId());
				checkPriveleges(pm, project);
				versionsList.add(version);
			}
			pm.deletePersistentAll(versionsList);
		} catch (JDOObjectNotFoundException e) {
			throw new ObjectAccessErrorException();
		} finally {
			if (tx.isActive()) {
				tx.rollback();
			}
			pm.close();
		}		
	}

	@Override
	public void rename(VersionInfo versionInfo) throws ObjectAccessErrorException, ConstraintViolationException {
		checkLoggedIn();
		new Validator().validate(versionInfo);

		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			Version version = pm.getObjectById(Version.class, versionInfo.getId());
			Project project = pm.getObjectById(Project.class, version.getProjectId());
			checkPriveleges(pm, project);

			version.setName(versionInfo.getName());
		} catch (JDOObjectNotFoundException e) {
			throw new ObjectAccessErrorException();
		} finally {
			pm.close();
		}		
	}
}
